package logsvr

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

const (
	maxLoggers = 1024
)

var root zap.Logger
var loggers = make([]zap.SugaredLogger, 0, maxLoggers)
var names []string
var glLevel zap.AtomicLevel

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
	type errorResponse struct {
		Error string `json:"error"`
	}
	type payload struct {
		Level *zapcore.Level `json:"level"`
	}

	enc := json.NewEncoder(w)

	switch r.Method {

	case http.MethodGet:
		current := glLevel.Level()
		enc.Encode(payload{Level: &current})

	case http.MethodPut:
		var req payload

		if err := func() string {
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				return fmt.Sprintf("Request body must be well-formed JSON: %v", err)
			}
			if req.Level == nil {
				return "Must specify a logging level."
			}
			return ""
		}(); err != "" {
			w.WriteHeader(http.StatusBadRequest)
			enc.Encode(errorResponse{Error: err})
			return
		}

		glLevel.SetLevel(*req.Level)
		enc.Encode(req)

	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
		enc.Encode(errorResponse{
			Error: "Only GET and PUT are supported.",
		})
	}
}

/*
输出的可读日志，无需毫秒
*/
func timeDevEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05"))
}

// 输出的 json 日志，有毫秒标识
func timeProductEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}

// LogInit 初始化日志系统
func LogInit(filename, level string) {
	level = strings.ToUpper(level)
	glLevel = zap.NewAtomicLevel()
	err := glLevel.UnmarshalText([]byte(level))
	if err != nil {
		fmt.Println("level string failed, set info default")
		glLevel.SetLevel(zapcore.DebugLevel)
	}

	hook := lumberjack.Logger{
		Filename:   filename,
		MaxSize:    1024,
		MaxBackups: 3,
		MaxAge:     7,
		Compress:   false,
	}

	pCfg := zap.NewProductionEncoderConfig()
	pCfg.EncodeTime = timeProductEncoder

	dCfg := zap.NewDevelopmentEncoderConfig()
	dCfg.EncodeTime = timeDevEncoder

	core := zapcore.NewTee(
		zapcore.NewCore(
			zapcore.NewJSONEncoder(pCfg),
			zapcore.AddSync(&hook),
			glLevel,
		),
		zapcore.NewCore(
			zapcore.NewConsoleEncoder(dCfg),
			zapcore.Lock(os.Stdout),
			glLevel,
		),
	)

	// init root && loggers
	root = *zap.New(core, zap.AddCaller())
	for pos := range names {
		loggers[pos] = *root.Named(names[pos]).Sugar()
		//fmt.Printf("init =>[%s] [%p]\n", names[pos], &(loggers[pos]))
	}
}

func GetLogger(name string) *zap.SugaredLogger {
	names = append(names, name)
	if len(names) >= maxLoggers {
		fmt.Println("ERROR, too much loggers!!!")
	}
	loggers = append(loggers, *root.Named(name).Sugar())
	return &loggers[len(loggers)-1]
}
